#include <stdio.h>                                                             
#include <stdlib.h>      
#include <math.h>                                                      
#include <pthread.h>                                                           
#include <string.h>     
#include <semaphore.h>
#include <assert.h>
#include <stddef.h>                                                          
#include <sys/types.h>                                                         
#include <sys/ipc.h>                                                           
#include <sys/shm.h>      
#include <sys/msg.h>      
#include "server.h" 

void * buffer;
int * producer;
int * consumer;
int msgid, shmid;
struct request *bufferRing;
pthread_mutex_t bufferRingMutex = PTHREAD_MUTEX_INITIALIZER;
sem_t sema;
typedef struct clientlist shmgetlist;

//compute function
//int compute(int x,int p);

//mod_pow
long mod_pow(int pow, int mod);

int main(int argc, char * argv[])
{
	int pid = getpid();
  	int counter,size,dt;
	size=0;
  	key_t key, shmkey;
  	struct my_msgbuf msgToSend;
	shmgetlist *curr,*head,*first;
	head=NULL;
	int buffSize = atoi(argv[1]);
	//int nClients = atoi(argv[2]);
	//int nThreads = atoi(argv[3]);
	//get message queue id
	key = ftok(".",'b');
	msgid = msgget(key, 0666);
	fprintf(stderr, "server got msgid %d\n", msgid);
  	if (msgid == -1){
	  perror("server msgget");
	  exit(1);
  	}
	fprintf(stderr,"Server ready\n");
	
	counter=0;

	size=msgrcv(msgid,&msgToSend,sizeof(int),0,0);
	assert(size!=-1);
	//while there are messages from clients in the queue process the first round of first requests
	while(size!=-1)
	{	
		//get message from queue using msgid
		if (size==-1){
	  		perror("server msgrcv");
	  		exit(1);
		}
		assert(size!=-1);
		//get shmid from message queue
		curr=(shmgetlist *)malloc(sizeof(shmgetlist));
		curr->shmid = msgToSend.shmid;
		curr->flag=0;
		curr->next=head;
		if(head==NULL)
		first=curr;
		head=curr;
		fprintf(stderr,"Server got shmid %d\n",curr->shmid);
		
		//attach the shared memory segment
		buffer = shmat(curr->shmid, (void *) NULL, 0);
		if (buffer == (void *)-1){
	  		perror("server shmat");
	  		exit(1);
		}
		//initialize producer consumer pointers
		producer = buffer;
		consumer=((int *)buffer) +1;
		bufferRing = (struct request *)((int*)buffer + 2);
		while (*producer == -1){
		//wait for a request
	  		usleep(1000);
		}
		fprintf(stderr,"Producer value %d\n",*producer);
		if(*producer==1) //service first request
		{
			bufferRing[0].result = mod_pow(bufferRing[0].input,bufferRing[0].p);
			fprintf(stderr,"Server placed value %ld at %d\n",bufferRing[0].result,(*consumer));
			*consumer = 1;
		}
		//while(*producer!=-2) //client still has requests pending
	  	//{
		//else if(*producer!=-2 && ((*consumer) % buffSize)  == *producer)
		//{
	      	//	goto repeat; //wait for client to place request
	    	//}
		//else if(*producer!=-2)
		//{	
	    		//bufferRing[*consumer].result = compute(bufferRing[*consumer].input,30); //satisfy request 
	    	//fprintf(stderr, "Placed a value at %d\n", (*consumer));
		//if(nThreads==1)
			//shmgetlist[counter].flag=1; //client done
		//*consumer= ((*consumer) + 1) % buffSize;
	 	dt=shmdt((void *)buffer); //detach current buffer segment
		//move to next client process
		size=msgrcv(msgid,&msgToSend, sizeof(int),0,0 | IPC_NOWAIT);
		counter++;
					
	}
	first->next=curr;
	curr=head;
	//process remaining requests
	while(curr->flag!=1)
	{	
		shmid=curr->shmid;
		fprintf(stderr,"shmid %d\n",shmid);
		//attach to the shared memory segment
		buffer = shmat(shmid, (void *) NULL, 0);
		if (buffer == (void *)-1){
	  		perror("server shmat");
	  		exit(1);
		}
		fprintf(stderr,"attached to shmid : %d\n",shmid);
		//initialize producer consumer pointers
		producer = buffer;
		consumer=((int *)buffer) +1;
		bufferRing = (struct request *)((int*)buffer + 2);
       		while((*consumer) == (*producer)) //wait for a request
		{
			usleep(1000);
		}	
		//else
		{
			bufferRing[*consumer].result=mod_pow(bufferRing[*consumer].input,bufferRing[*consumer].p);
			fprintf(stderr,"Server placed a value %ld at %d\n",bufferRing[*consumer].result,(*consumer));
			*consumer = ((*consumer + 1) % buffSize);
		}
		if(*producer==-2)
		{
		//client finished
			curr->flag=1;
		}
		dt=shmdt((void *)buffer);
		curr=curr->next;
	}

	fprintf(stderr, "server done!\n");
}

/*int compute(int x,int p)
{
	int result,base;
	base=pow(2,x);
	result = (base % p);
	return result;
}*/
long mod_pow(int pow, int mod)
{
    unsigned long long test;
    unsigned long long n = 2;
    for(test = 1; pow; pow >>= 1)
    {
        if (pow & 1)
            test = ((test % mod) * (n % mod)) % mod;
        n = ((n % mod) * (n % mod)) % mod;
    }

    return test; /* note this is potentially lossy */
}

